# -*- coding: utf-8 -*-
"""
车牌识别服务Web应用

基于WSGI的车牌识别Web服务，提供RESTful API接口用于：
- 车牌检测和识别
- 多种输入格式支持（URL、Base64、文件上传）
- 批量处理
- 健康检查

API接口：
- GET  /        : 健康检查
- POST /        : 车牌识别服务

Author: AI Assistant  
Date: 2025-06-12
"""

from urllib.parse import parse_qs
import json
import time
import base64
import os
import random
import traceback

import multipart as mp

from LogHelper import logger
import lprservice

# 全局服务实例
license_plate_service = lprservice.LicensePlateRecognitionService()

# 配置常量
UPLOAD_DIRECTORY = "./upload"
os.makedirs(UPLOAD_DIRECTORY, exist_ok=True)

def save_base64_image(base64_image_data):
    """
    保存Base64编码的图像数据到本地文件
    
    Args:
        base64_image_data: Base64编码的图像数据字符串
        
    Returns:
        保存的图像文件路径
    """
    try:
        # 提取Base64数据部分
        if ';base64,' in base64_image_data:
            b64_data = base64_image_data.split(';base64,')[1]
        else:
            b64_data = base64_image_data
            
        # 解码Base64数据
        image_data = base64.b64decode(b64_data)
        
        # 生成唯一文件名
        timestamp = int(time.time())
        random_id = random.randint(1000000, 9999999)
        filename = f'{timestamp}_{random_id}.jpg'
        file_path = os.path.join(UPLOAD_DIRECTORY, filename)
        
        # 保存文件
        with open(file_path, 'wb') as f:
            f.write(image_data)
            
        logger.info(f"Base64图像已保存到: {file_path}")
        return file_path
        
    except Exception as e:
        logger.error(f"保存Base64图像失败: {e}")
        raise ValueError(f"无法保存Base64图像: {e}")

def perform_license_plate_recognition(image_path, config=None):
    """
    执行车牌识别
    
    Args:
        image_path: 图像文件路径
        config: 识别配置对象
        
    Returns:
        识别结果字典，包含errcode和data字段
    """
    try:
        recognition_results = license_plate_service.recognize_license_plates(
            image_path, config=config
        )
        return {"errcode": 0, "data": recognition_results}
    except Exception as error:
        logger.error(f"车牌识别失败: {traceback.format_exc()}")
        return {"errcode": -2, "errinfo": str(error)}

def create_recognition_config(request_params):
    """
    根据请求参数创建识别配置
    
    Args:
        request_params: 请求参数字典
        
    Returns:
        LicensePlateConfig配置对象
    """
    config = lprservice.LicensePlateConfig()
    
    # 基础配置映射
    config_mappings = {
        'save': 'image_save',
        'return_src': 'return_src', 
        'return_box': 'return_box',
        'filter_error_plate': 'filter_error_plate',
        'need_detect': 'need_detect',
        'need_transformer': 'need_transformer',
        'need_color': 'need_color',
        'chinese_thres': 'chinese_thres',
        'chinese_thres_for_char': 'chinese_thres_for_char'
    }
    
    # 应用整数类型配置
    for param_key, config_key in config_mappings.items():
        if param_key in request_params:
            try:
                setattr(config, config_key, int(request_params[param_key]))
            except ValueError:
                logger.warning(f"参数 {param_key} 值无效: {request_params[param_key]}")
    
    # 应用字符串类型配置
    string_configs = {
        'priority_province_chinese': 'priority_province_chinese',
        'priority_province_char': 'priority_province_char',
        'detect_model': 'detect_model',
        'plate_resize_type': 'plate_resize_type',
        'lpr_type': 'lpr_type'
    }
    
    for param_key, config_key in string_configs.items():
        if param_key in request_params:
            setattr(config, config_key, request_params[param_key])
    
    return config

def process_form_data_request(environ):
    """
    处理form-data格式的请求
    
    Args:
        environ: WSGI环境变量
        
    Returns:
        处理后的参数字典，包含file_list字段
    """
    params, uploaded_files = mp.parse_form_data(environ)
    file_list = []
    
    for file_key in uploaded_files:
        uploaded_file = uploaded_files[file_key]
        file_path = os.path.join(UPLOAD_DIRECTORY, uploaded_file.filename)
        
        logger.info(f"保存上传文件到: {file_path}")
        uploaded_file.save_as(file_path)
        
        file_list.append({
            'name': uploaded_file.name,
            'file_path': file_path
        })
    
    params['file_list'] = file_list
    return params

def process_json_request(environ):
    """
    处理JSON格式的请求
    
    Args:
        environ: WSGI环境变量
        
    Returns:
        解析后的参数字典
    """
    try:
        content_length = int(environ.get('CONTENT_LENGTH', 0))
        if content_length == 0:
            return {}
            
        request_body = environ['wsgi.input'].read(content_length)
        body_string = request_body.decode("utf-8")
        return json.loads(body_string)
    except (ValueError, json.JSONDecodeError) as e:
        logger.error(f"JSON请求解析失败: {e}")
        raise ValueError("无效的JSON数据")

def process_single_image_recognition(image_source, config, recognition_type="url"):
    """
    处理单个图像的识别请求
    
    Args:
        image_source: 图像源（URL、Base64数据或文件路径）
        config: 识别配置
        recognition_type: 识别类型 ("url", "base64", "file")
        
    Returns:
        识别结果
    """
    if recognition_type == "base64":
        image_path = save_base64_image(image_source)
    else:
        image_path = image_source
        
    return perform_license_plate_recognition(image_path, config)

def process_batch_recognition(image_list, config, is_base64_list=True):
    """
    处理批量图像识别请求
    
    Args:
        image_list: 图像列表
        config: 识别配置
        is_base64_list: 是否为Base64格式列表
        
    Returns:
        批量识别结果
    """
    batch_results = []
    
    for image_item in image_list:
        try:
            if is_base64_list:
                # JSON格式的Base64图像列表
                image_path = save_base64_image(image_item)
                result = perform_license_plate_recognition(image_path, config)
                batch_results.append(result['data'] if result["errcode"] == 0 else [])
            else:
                # form-data格式的文件列表
                image_path = image_item['file_path']
                image_key = image_item['name']
                result = perform_license_plate_recognition(image_path, config)
                
                result_data = {image_key: result['data'] if result["errcode"] == 0 else []}
                batch_results.append(result_data)
                
        except Exception as e:
            logger.error(f"批量识别中的图像处理失败: {e}")
            if is_base64_list:
                batch_results.append([])
            else:
                batch_results.append({image_item.get('name', 'unknown'): []})
    
    return {"errcode": 0, "data": batch_results}

def process_batch_file_recognition(file_path_list, config):
    """
    处理批量文件路径识别请求（JSON格式）
    
    Args:
        file_path_list: 文件路径字符串列表
        config: 识别配置
        
    Returns:
        批量识别结果
    """
    batch_results = []
    
    for file_path in file_path_list:
        try:
            # 检查文件是否存在
            if not os.path.exists(file_path):
                logger.warning(f"文件不存在: {file_path}")
                batch_results.append([])
                continue
                
            result = perform_license_plate_recognition(file_path, config)
            batch_results.append(result['data'] if result["errcode"] == 0 else [])
                
        except Exception as e:
            logger.error(f"批量文件识别中的处理失败 {file_path}: {e}")
            batch_results.append([])
    
    return {"errcode": 0, "data": batch_results}

def application(environ, start_response):
    """
    WSGI应用入口点
    
    处理HTTP请求并返回车牌识别结果或健康检查信息
    
    Args:
        environ: WSGI环境变量
        start_response: WSGI响应函数
        
    Returns:
        响应数据字节列表
    """
    try:
        # 健康检查端点 - 只有GET请求到根路径才返回健康检查
        if (environ['PATH_INFO'] == '/health' or 
            (environ['PATH_INFO'] == '/' and environ['REQUEST_METHOD'] == 'GET')):
            start_response('200 OK', [('Content-Type', 'application/json')])
            health_response = {"status": "healthy", "service": "lpr-service"}
            return [json.dumps(health_response).encode('utf-8')]

        # 初始化响应数据
        response_data = {"errcode": -9999, "errinfo": "未知错误"}
        transparent_data = None  # 透传数据

        # 解析请求参数
        if environ['REQUEST_METHOD'] == 'GET':
            # 处理GET请求
            query_string = environ['QUERY_STRING']
            if query_string:
                params = dict([param.split('=', 1) for param in query_string.split('&')])
            else:
                params = {}
        else:
            # 处理POST请求
            content_type = environ.get('CONTENT_TYPE', '')
            if 'form-data' in content_type:
                params = process_form_data_request(environ)
            else:
                params = process_json_request(environ)

        logger.info(f"接收到请求参数: {list(params.keys())}")

        # 创建识别配置
        recognition_config = create_recognition_config(params)

        # 提取透传数据
        if 'trans_data' in params:
            transparent_data = params['trans_data']

        # 根据不同的输入类型进行识别
        if 'url' in params:
            # URL方式识别
            response_data = process_single_image_recognition(
                params['url'], recognition_config, "url"
            )
        elif 'image_data' in params:
            # Base64数据识别
            response_data = process_single_image_recognition(
                params['image_data'], recognition_config, "base64"
            )
        elif 'image_list' in params:
            # 批量Base64数据识别
            response_data = process_batch_recognition(
                params['image_list'], recognition_config, is_base64_list=True
            )
        elif 'file_list' in params:
            # 批量文件识别 - JSON格式的文件路径列表
            file_list = params['file_list']
            if isinstance(file_list, list) and len(file_list) > 0:
                # 检查是否为文件路径字符串列表（JSON格式）还是文件对象列表（form-data格式）
                if isinstance(file_list[0], str):
                    # JSON格式：文件路径字符串列表
                    response_data = process_batch_file_recognition(
                        file_list, recognition_config
                    )
                else:
                    # form-data格式：文件对象列表
                    response_data = process_batch_recognition(
                        file_list, recognition_config, is_base64_list=False
                    )
            else:
                response_data = {"errcode": -1, "errinfo": "file_list为空或格式错误"}
        else:
            response_data = {
                "errcode": -1, 
                "errinfo": "缺少必需的参数：url、image_data、image_list或file_list"
            }

    except Exception as error:
        logger.error(f"请求处理异常: {traceback.format_exc()}")
        response_data = {"errcode": -999, "errinfo": f"服务异常: {str(error)}"}

    # 设置响应头
    start_response('200 OK', [('Content-Type', 'application/json')])

    # 添加透传数据
    if transparent_data:
        response_data['trans_data'] = transparent_data

    # 返回JSON响应
    return [json.dumps(response_data, ensure_ascii=False).encode('utf-8')]

if __name__ == '__main__':
    """
    本地测试入口
    """
    try:
        # 测试图像路径
        test_image_paths = [
            'test/1.jpg',
            'test/1_0.jpg'
        ]
        
        for image_path in test_image_paths:
            if os.path.exists(image_path):
                print(f"\n测试图像: {image_path}")
                
                # 创建测试配置
                test_config = lprservice.LicensePlateConfig()
                test_config.show = False
                test_config.need_detect = 1
                
                # 执行识别
                results = license_plate_service.recognize_license_plates(
                    image_path, 
                    show_result=False, 
                    config=test_config
                )
                
                print(f"识别结果: {results}")
                break
            else:
                print(f"测试图像不存在: {image_path}")

    except Exception as error:
        print(f"测试异常: {error}")
        traceback.print_exc()
